A common way to protect a server from the access of malicious is to identify the client; in my opinion, the best way to do that is the mutual SSL authentication. To understand what is the mutual SSL Authentication and other good practices for the protection of an endpoint you can read this article.

You can implement two-way authentication SSL using a WEB Server, for this example I used apache web server.

In the web there are more abstract examples of configuring two-way authentication SSL with Apache for development environment, but no one has a complete example. I hope this is quite complete!

Yes, I’m talking about development environment, because usually in this step certificates are self signed and there is much more work to do (you have to simulate a CA and create certificates).

Here there are the three marco steps:

  1. Create the server certificate
  2. Create the client certificate and the PKCS12 container
  3. Configure the apache web server

1. Create the server certificate

Before creating a certificate, you have to create a CA:

Create the structure directory and protect from other users:

mkdir ssl
chmod 0700 ssl
cd ssl
mkdir certs private

Create a database to keep track of each certificate signed:

echo '100001' > serial
touch certindex.txt

Make a custom config file for openssl to use:

#
# OpenSSL configuration file.
#

# Establish working directory.

dir = .

[ ca ]
default_ca = CA_default

[ CA_default ]
serial = $dir/serial
database = $dir/certindex.txt
new_certs_dir = $dir/certs
certificate = $dir/cacert.pem
private_key = $dir/private/cakey.pem
default_days = 365
default_md = md5
preserve = no
email_in_dn = no
nameopt = default_ca
certopt = default_ca
policy = policy_match

[ policy_match ]
countryName = match
stateOrProvinceName = match
organizationName = match
organizationalUnitName = optional
commonName = supplied
emailAddress = optional

[ req ]
default_bits = 2048 # Size of keys
default_keyfile = key.pem # name of generated keys
default_md = md5 # message digest algorithm
string_mask = nombstr # permitted characters
distinguished_name = req_distinguished_name
req_extensions = v3_req

[ req_distinguished_name ]
countryName = Country Name (2 letter code)
countryName_default = IT
countryName_min = 2
countryName_max = 2

stateOrProvinceName = State or Province Name (full name)
stateOrProvinceName_default = Italy

localityName = Locality Name (eg, city)
localityName_default = Milan

0.organizationName = Organization Name (eg, company)
0.organizationName_default = Organization default

# we can do this but it is not needed normally :-)
#1.organizationName = Second Organization Name (eg, company)
#1.organizationName_default = World Wide Web Pty Ltd

organizationalUnitName = Organizational Unit Name (eg, section)
organizationalUnitName_default = Organization unit name default

commonName = Common Name (eg, YOUR name)
commonName_max = 64
commonName_default = www.stefanocapitanio.com

emailAddress = Email Address
emailAddress_max = 64

# SET-ex3 = SET extension number 3

[ req_attributes ]
challengePassword = A challenge password
challengePassword_min = 4
challengePassword_max = 20

unstructuredName = An optional company name

[ req_distinguished_name ]
countryName = Country Name (2 letter code)
countryName_default = IT
countryName_min = 2
countryName_max = 2

stateOrProvinceName = State or Province Name (full name)
stateOrProvinceName_default = Italy

localityName = Locality Name (eg, city)

0.organizationName = Organization Name (eg, company)
0.organizationName_default = Organization default

# we can do this but it is not needed normally :-)
#1.organizationName = Second Organization Name (eg, company)
#1.organizationName_default = World Wide Web Pty Ltd

organizationalUnitName = Organizational Unit Name (eg, section)
organizationalUnitName_default = Organization unit default

commonName = Common Name (eg, YOUR name)
commonName_max = 64
commonName_default = www.stefanocapitanio.com

emailAddress = Email Address
emailAddress_max = 64

# SET-ex3 = SET extension number 3

[ req_attributes ]
challengePassword = A challenge password
challengePassword_min = 4
challengePassword_max = 20

unstructuredName = An optional company name
[ v3_ca ]
basicConstraints = CA:TRUE
subjectKeyIdentifier = hash
authorityKeyIdentifier = keyid:always,issuer:always

[ v3_req ]
basicConstraints = CA:FALSE
subjectKeyIdentifier = hash

Create root certificate:

openssl req -new -x509 -extensions v3_ca -keyout private/cakey.pem -out cacert.pem -days 365 -config ./openssl.cnf

This script will create private/cakey.pem  that is the private key of the CA and the cacert.pem that is the  which is the one you can give to others for import in their browsers.

Create a key and signing request for the server

openssl req -new -nodes -out server-req.pem -keyout private/server-key.pem -days 365 -config ./openssl.cnf

The output of this script is server-req.pem that is the CSR and the private/server-key.pem that is the private key

Sign the request

openssl ca -out server-cert.pem -days 365 -config ./openssl.cnf -infiles server-req.pem

This will produce server-cert.pem that is the public certificate

2. Create the client certificate and the PKCS12 container

Create a key and signing request for each client

openssl req -new -nodes -out client-req.pem -keyout private/client-key.pem -days 365 -config ./openssl.cnf
*on the common name you have to specify a different name

Sign each request

openssl ca -out name-cert.pem -days 365 -config ./openssl.cnf -infiles client-req.pem

Create the PKCS12 file

openssl pkcs12 -export -in name-cert.pem -inkey private/name-key.pem -certfile cacert.pem -name "[friendly name]" -out name-cert.p12

The produced file is the file that the server will require to comunicate, and you have to install on the client

3. Configure APACHE

After installing and configuring apache and enabling SSL, you can get a VirtualHost and configure them:

<VirtualHost *:7001>
  ServerAdmin name@yourdomain.com
  SSLEngine on
  SSLCertificateFile /home/sempla1/ssl/server-cert.pem
  SSLCertificateKeyFile /home/sempla1/ssl/private/server-key.pem

  SSLVerifyClient require
  SSLVerifyDepth 10
  SSLCACertificateFile /home/sempla1/ssl/cacert.pem
  <location />
    Order allow,deny
    allow from all
    SSLRequire (%{SSL_CLIENT_S_DN_CN} eq "clientcn")
 </location>
  ProxyPass / http://127.0.0.1:8080/
  ProxyPassReverse / http://127.0.0.1:8080/
</VirtualHost>

One-way  (standard) SSL auth configuration:

SSLEngine on -> to enable the single way SSL authentication

SSLCertificateFile -> to specify the public certificate that the WebServer will show to the users

SSLCertificateKeyFIle -> to specify the private key that will be used to encrypt the data sent

Two-way SSL auth configuration:

SSLVerifyClient -> to enable the two-way SSL authentication

SSLVerifyDepth -> to specify the depth of the check if the certificate has an approved CA

SSLCACertificateFile -> the public key that will be used to decrypt the data recieved

SSLRequire -> Allows only requests that satisfy the expression

 

Ok, now you are ready to test the two-way SSL authentication in your browser: install the client certificate (.p12 file) and add the CA in the trusted CA (to avoid the alert on the not secure https connection).

 

Now you can try to call a web service (with CXF and JAVA):

Download server certificate server.crt and take the client X509 certificate client-cert.p12.

Convert .p12 file in a Java Key Store using keytool JDK:

$JAVA_HOME/bin/keytool -importkeystore -srckeystore client-cert.p12 -srcstoretype PKCS12 -destkeystore keyManager.jks -deststoretype JKS

With keytool you can add the server certificate to a trust manager keystore:

$JAVA_HOME/bin/keytool -import -alias server-file server.crt -keystore trustManager.jks

Now you can configure on CXF configuration the call to the web service:

<http-conf:conduit name="*.http-conduit">
  <http-conf:tlsClientParameters secureSocketProtocol="SSL" disableCNCheck="true" >
    <sec:keyManagers keyPassword="123456">
      <sec:keyStore type="jks" password="123456" file="C:/var/lib/jbossas/p2p/certificati/keyManager.jks" />
    </sec:keyManagers>
    <sec:trustManagers>
      <sec:keyStore type="jks" password="123456" file="C:/var/lib/jbossas/p2p/certificati/trustManager.jks" />
    </sec:trustManagers>
    <sec:cipherSuitesFilter>
    <sec:include>.*_EXPORT_.*</sec:include>
      <sec:include>.*_EXPORT1024_.*</sec:include>
      <sec:include>.*_WITH_DES_.*</sec:include>
      <sec:include>.*_WITH_AES_.*</sec:include>
      <sec:include>.*_WITH_NULL_.*</sec:include>
      <sec:exclude>.*_DH_anon_.*</sec:exclude>
    </sec:cipherSuitesFilter>
  </http-conf:tlsClientParameters>
</http-conf:conduit>

A big thank you to www.flatmtn.com, I used its guides to start creating CA, keys and certificates.

UPDATED: Thanks to @anjana_j for pointing out a mistake!